﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Nes.Core.APUS
{
    /// <summary>
    /// 噪音
    /// </summary>
    public class Noise
    {
        private static readonly Random random = new Random();
        public int LengthCounter { get; internal set; }
        private Boolean _enable;
        public bool Enable
        {
            get
            {
                return _enable;
            }
            set
            {
                _enable = value;
                if (!Enable)
                {
                    LengthCounter = 0;
                }
            }
        }
        public Int32 Volume { get; internal set; }

        private Int32 Timer;
        private Boolean IsLoopNoise;
        private Int32 NosePeriod;
        private Int32 InternalTimer;
        private Boolean LengthCounterHalt;
        private Boolean IsConstantVolume;

        private Int32 EnvelopeValue;
        private Int32 EnvelopeVolume;
        private Int32 EnvelopeCounter;

        internal void Clock()
        {
            if (!Enable)
            {
                return;
            }
            if (InternalTimer == 0)
            {
                InternalTimer = Timer;
                Step();
            }
            else
            {
                InternalTimer--;
            }
        }

        private void Step()
        {
            if(!Enable || LengthCounter == 0)
            {
                Volume = 0;
            }
            else if (IsConstantVolume)
            {
                Volume = (Int32)Math.Floor(EnvelopeValue * random.NextSingle());
            }
            else
            {
                Volume = (Int32)Math.Floor(EnvelopeVolume * random.NextSingle());
            }
        }

        internal void ProcessEnvelope()
        {
            if (IsConstantVolume)
            {
                return;
            }
            if (EnvelopeCounter % (EnvelopeValue + 1) == 0)
            {
                if (EnvelopeVolume == 0)
                {
                    EnvelopeVolume = LengthCounterHalt ? 15 : 0;
                }
                else
                {
                    EnvelopeVolume--;
                }
            }
            EnvelopeCounter++;
        }

        internal void ProcessLengthCounter()
        {
            if(!LengthCounterHalt && LengthCounter > 0)
            {
                LengthCounter--;
            }
        }

        internal void Write(Int32 addr, Byte data)
        {
            switch (addr)
            {
                case 0: // --lc vvvv
                    LengthCounterHalt = (data & 0b0010_0000) > 0;
                    IsConstantVolume = (data & 0b0001_0000) > 0;
                    EnvelopeValue = (data & 0b0000_1111);
                    EnvelopeVolume = 15;
                    EnvelopeCounter = 0;
                    break;
                case 1: // Unused
                    break;
                case 2: // M---.PPPP
                    IsLoopNoise = (data & 0b1000_0000) > 0;
                    NosePeriod = CommonProp.NoisePeroidTable[data & 0b0000_1111];
                    InternalTimer = 0;
                    break;
                case 3: // LLLL LTTT	Length counter load (L), timer high (T), set linear counter reload flag
                    LengthCounter = CommonProp.LengthTable[data >> 3];
                    break;
            }
        }
    }
}
